<?php

namespace api\services;

use api\components\FileServer;
use api\services\wechat\WechatCp;
use api\services\wechat\WechatMiniprogram;
use api\utils\LogUtil;
use Exception;
use SplFileObject;
use Yii;
use yii\helpers\FileHelper;

class WechatService
{
    const GET_MEDIA_PATH = '/media/get';

    /**
     * @var WechatInterface
     */
    private $channel;

    private $env;
    // 产线
    private $app;
    // 小程序
    private $channelId;

    public function __construct($env = 'wechat', $app = '', $channelId = '')
    {
        $this->env = $env;
        $this->app = $app;
        $this->channelId = $channelId;
        switch ($env) {
            case 'zw':
                $this->channel = new WechatCp();
                break;
            case 'wechat':
            case 'miniprogram':
                $this->channel = new WechatMiniprogram($app, $channelId);
                break;
            default:
                throw new Exception('Unsupport type: ' . $env);
        }
    }

    public function loginByWechatCode($code, $category = 'zw')
    {
        $access_token = $this->getAccessToken();
        $wechatId = $this->channel->getWechatUser($access_token, $code);

        $info = Yii::$app->userCenter->loginByWechatId($wechatId, $category);

        return $info;
    }

    public function getTicketSignature($url)
    {
        $jsticket = $this->getJsTicket();

        // 进行sha1签名
        $timestamp = time();
        $nonceStr = $this->createNonceStr();
        $str = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
        $sha_str = sha1($str);

        $appid = $this->channel->getJsTicketAppId();

        return [
            'appId' => $appid,
            'nonceStr' => $nonceStr,
            'timestamp' => $timestamp,
            'signature' => $sha_str,
        ];
    }

    public function createNonceStr($length = 16)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    public function getJsTicket()
    {
        $redis = Yii::$app->redisCache;
        $cache_name = $this->env . ':' . $this->app . ':' .  $this->channelId . ':jsapi_ticket';
        $jsticket = $redis->get($cache_name);
        if (!empty($jsticket)) {
            return $jsticket;
        }
        $access_token = $this->getAccessToken('jsapi_ticket');
        $url = $this->channel->getJsTicketUrl($access_token);
        $response = Yii::$app->curl->get($url);
        if ($response['errcode'] == 0 && $response['errmsg'] == 'ok') {
            $redis->set($cache_name, $response['ticket'], $response['expires_in'] - 300);
            return $response['ticket'];
        } elseif (!empty($response['ticket'])) {
            return $response['ticket'];
        } else {
            throw new Exception('get js_ticket error: ' . json_encode($response));
        }
    }

    public function getAccessToken($path = '', $useCache = true)
    {
        $redis = Yii::$app->redisCache;
        $cache_name = $this->env . ':' . $path . ':' . $this->app . ':' .  $this->channelId .  ':access_token';
        $token = $redis->get($cache_name);
        if ($useCache && !empty($token)) {
            return $token;
        }
        if ($this->channel instanceof WechatMiniprogram) {
            $url = $this->channel->getBindWechatAccessTokenUrl();
        } else {
            $url = $this->channel->getAccessTokenUrl($this->channelId);
        }
        $response = Yii::$app->curl->get($url);
//        print_r($response);exit;
        $expired = ($response['expires_in'] ?? 7200) - 300;
        if ($response['errcode'] == 0 && $response['errmsg'] == 'ok') {
            $redis->set($cache_name, $response['access_token'], $expired);
            return $response['access_token'];
        } elseif (!empty($response['access_token'])) {
            $redis->set($cache_name, $response['access_token'], $expired);
            return $response['access_token'];
        } else {
            throw new Exception('get access_token error: ' . json_encode($response));
        }
    }

    public function getTemplateMedia($mediaId, $type)
    {
        $access_token = $this->getAccessToken();
        $url = $this->channel->getHost() . self::GET_MEDIA_PATH . '?access_token=' . $access_token . '&media_id=' . $mediaId;
        return self::downFileFromWechat($url, $type);
    }

    public static function downFileFromWechat($url, $type)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, TRUE);
        curl_setopt($ch, CURLOPT_NOBODY, FALSE);
        $response = curl_exec($ch);
        $header = '';
        $body = '';
        if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
            $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
            $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
            $header = substr($response, 0, $headerSize);
            $body = substr($response, $headerSize);
        } else {
            LogUtil::warn(['message' => 'get wechat template failed', 'zw']);
            throw new Exception('文件无法获取');
        }
        curl_close($ch);
        if (!preg_match('/filename="(.*?)"/', $header, $arr)) {
            LogUtil::warn(['message' => 'get file by media failed', 'url' => $url, 'header' => $header, 'body' => $body]);
            return false;
        }
        $file = str_replace("wxLocalResource://", "", $arr[1]);
        switch ($type) {
            case 'audio':
                $fileType = FileServer::FILE_TYPE_AUDIO;
                $originFile = str_replace("wxLocalResource://", "", $arr[1]);
                $file = strpos($originFile, '.mp3') != false ? $originFile : $originFile . '.' . self::getExtensionByAudio($content_type);
                break;
            case 'video':
                $fileType = FileServer::FILE_TYPE_VEDIO;
                $originFile =  str_replace("wxLocalResource://", "", $arr[1]);
                $file = strpos($originFile, '.mp4') != false ?  $originFile : ($originFile. '.' . self::getExtensionByVideo($content_type));
                break;
            case 'image':
                $fileType = FileServer::FILE_TYPE_IMG;
                break;
            default:
                break;
                // throw new Exception('暂不支持的文件类型');
        }
        $iTime = time();
        $file = $iTime . '_' . $file;
        $filePath = Yii::getAlias(Yii::$app->params['uploadPath']);
        FileHelper::createDirectory($filePath);
        $fullName = $filePath . $file;
        @chmod($fullName, 0755);
        if (file_put_contents($fullName, $body)) {
            // $sText = 'APP' . date('Y-m-d H:i:s');
            // self::makeShuiYin($fullName,$sText);
            return Yii::$app->fileServer->uploadServerFile(new SplFileObject($fullName), $fileType, null, [], $file);
        }
        return false;
    }

    public function downloadWechatImageBase64($base64_image_content)
    {
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)){
            $type = $result[2];
            $file = uniqid('wxbase64') . '.' . $type;
            $iTime = time();
            $file = $iTime . '_' . $file;
            $filePath = Yii::getAlias(Yii::$app->params['uploadPath']);
            FileHelper::createDirectory($filePath);
            $fullName = $filePath . $file;
            @chmod($fullName, 0755);
            if (file_put_contents($fullName, base64_decode(str_replace($result[1], '', $base64_image_content)))) {
                // $sText = 'APP'.date('Y-m-d H:i:s');
                // self::makeShuiYin($fullName,$sText);
                return Yii::$app->fileServer->uploadServerFile(new SplFileObject($fullName), FileServer::FILE_TYPE_IMG, null, [], $file);
            }
        }
        return false;
    }

    public static function getExtensionByAudio($content_type) {
        $mapping_arr = [
            'audio/amr' => 'amr',
            'audio/mp3' => 'mp3',
        ];
        return isset($mapping_arr[$content_type]) ? $mapping_arr[$content_type] : 'amr';
    }

    public static function getExtensionByVideo($content_type) {
        $mapping_arr = [
            'video' => 'mp4',
        ];
        return isset($mapping_arr[$content_type]) ? $mapping_arr[$content_type] : 'mp4';
    }

    /**
     * @see https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
     * POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN
     */
    public function sendSubscribeMessage($template = [], $user = [], $eventData = [])
    {
        $access_token = $this->getAccessToken();

        return $this->channel->sendSubscribeMessage($access_token, $template, $user, $eventData);
    }

    public function sendMessage($userIds, $sendParams = [])
    {
        $access_token = $this->getAccessToken();

        return $this->channel->sendMessage($this->channelId, $userIds, $sendParams, $access_token);
    }
}
